package com.atguigu.gulimall.order.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.atguigu.common.constant.OrderConstant;
import com.atguigu.common.enums.OrderStatusEnum;
import com.atguigu.common.to.order.OrderTo;
import com.atguigu.common.utils.R;
import com.atguigu.common.utils.StringUtils;
import com.atguigu.common.vo.MemberResp;
import com.atguigu.gulimall.order.domain.response.CartItemResp;
import com.atguigu.gulimall.order.domain.response.FreightResp;
import com.atguigu.gulimall.order.domain.response.ReceiveAddressResp;
import com.atguigu.gulimall.order.domain.response.SkuHasStockResp;
import com.atguigu.gulimall.order.domain.to.CreateOrderTo;
import com.atguigu.gulimall.order.domain.vo.*;
import com.atguigu.gulimall.order.entity.MqMessageEntity;
import com.atguigu.gulimall.order.entity.OrderItemEntity;
import com.atguigu.gulimall.order.exception.OrderItemLockException;
import com.atguigu.gulimall.order.exception.UserNotLoggedInException;
import com.atguigu.gulimall.order.feign.CartFeignService;
import com.atguigu.gulimall.order.feign.MemberFeignService;
import com.atguigu.gulimall.order.feign.ProductFeignService;
import com.atguigu.gulimall.order.feign.WareSkuFeignService;
import com.atguigu.gulimall.order.rabbit.constant.OrderRabbitConstant;
import com.atguigu.gulimall.order.service.MqMessageService;
import com.atguigu.gulimall.order.service.OrderItemService;
import com.atguigu.gulimall.order.utils.AlipayTradeClose;
import com.atguigu.gulimall.order.utils.LoginUserUtils;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.scripting.support.ResourceScriptSource;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.atguigu.common.utils.PageUtils;
import com.atguigu.common.utils.Query;

import com.atguigu.gulimall.order.dao.OrderDao;
import com.atguigu.gulimall.order.entity.OrderEntity;
import com.atguigu.gulimall.order.service.OrderService;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

@Slf4j
@Service("orderService")
public class OrderServiceImpl extends ServiceImpl<OrderDao, OrderEntity> implements OrderService {

    @Autowired
    MemberFeignService memberFeignService;

    @Autowired
    CartFeignService cartFeignService;

    @Autowired
    WareSkuFeignService wareSkuFeignService;

    @Autowired
    ThreadPoolTaskExecutor threadPoolTaskExecutor;

    @Autowired
    StringRedisTemplate stringRedisTemplate;

    @Autowired
    ProductFeignService productFeignService;

    @Autowired
    OrderItemService orderItemService;

    @Autowired
    RabbitTemplate rabbitTemplate;

    @Autowired
    MqMessageService messageService;
    
    @Autowired
    AlipayTradeClose alipayTradeClose;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<OrderEntity> page = this.page(
                new Query<OrderEntity>().getPage(params),
                new QueryWrapper<OrderEntity>()
        );

        return new PageUtils(page);
    }

    /**
     * 获取订单确认页数据
     *
     * @return
     */
    @Override
    public OrderConfirmVO toTrade() {
        log.info("OrderServiceImpl.toTrade get Order Confirm Page data");
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        RequestContextHolder.setRequestAttributes(requestAttributes, true);
        Map<Long, Boolean> stocks = new HashMap<>();
        OrderConfirmVO orderConfirmVO = OrderConfirmVO.builder().stocks(stocks).build();

        //获取用户收货地址
        MemberResp loginUserInfo = getLoginUserInfo();
        CountDownLatch countDownLatch = new CountDownLatch(2);
        threadPoolTaskExecutor.execute(() -> {
            try {
                R memberResponse = memberFeignService.getAddressByMemberId(loginUserInfo.getId());
                Integer code = memberResponse.getCode();
                if (code == 0) {
                    List<ReceiveAddressResp> receiveAddressRespList = memberResponse.getData(new TypeReference<List<ReceiveAddressResp>>() {
                    });
                    log.info("OrderServiceImpl.toTrade get addresses data result: {}", JSONObject.toJSONString(receiveAddressRespList));
                    orderConfirmVO.setAddresses(receiveAddressRespList);
                }
            } finally {
                countDownLatch.countDown();
            }
        });
        //获取购物项信息
        threadPoolTaskExecutor.execute(() -> {
            try {
                R checkCatItemResponse = cartFeignService.getCheckCatItem();
                if (checkCatItemResponse.getCode() == 0) {
                    List<CartItemResp> cartItemRespList = checkCatItemResponse.getData(new TypeReference<List<CartItemResp>>() {
                    });
                    log.info("OrderServiceImpl.toTrade get check cartItem data result: {}", JSONObject.toJSONString(cartItemRespList));
                    orderConfirmVO.setCartItems(cartItemRespList);
                }
            } finally {
                countDownLatch.countDown();
            }
        });

        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            log.error("OrderServiceImpl.toTrade countDownLatch await exception", e);
        }
        //获取库存信息
        List<CartItemResp> cartItems = orderConfirmVO.getCartItems();
        if (cartItems != null && cartItems.size() > 0) {
            List<Long> skuIds = cartItems.stream().map(CartItemResp::getSkuId).collect(Collectors.toList());
            R skuHasStockResp = wareSkuFeignService.getSkuHasStock(skuIds);
            if (skuHasStockResp.getCode() == 0) {
                List<SkuHasStockResp> hasStock = skuHasStockResp.getData(new TypeReference<List<SkuHasStockResp>>() {
                });
                Map<Long, Boolean> stockMap = hasStock.stream().collect(Collectors.toMap(SkuHasStockResp::getSkuId, SkuHasStockResp::getHasStock));
                orderConfirmVO.setStocks(stockMap);
            }
        }
        String uuidToken = getUUIDToken();
        orderConfirmVO.setOrderToken(uuidToken);
        String orderTokenKey = OrderConstant.getOrderTokenKey(loginUserInfo.getId().toString());
        stringRedisTemplate.opsForValue().set(orderTokenKey, uuidToken, 30, TimeUnit.MINUTES);
        return orderConfirmVO;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public SubmitOrderRespVO submitOrder(SubmitOrderVO submitOrderVO) {
        //验证令牌
        String orderToken = submitOrderVO.getOrderToken();
        SubmitOrderRespVO submitOrderRespVO = new SubmitOrderRespVO();
        if (StringUtils.isEmpty(orderToken)) {
            submitOrderRespVO.setCode(1);
            return submitOrderRespVO;
        }
        String scriptStr = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        DefaultRedisScript<Long> longDefaultRedisScript = new DefaultRedisScript<>(scriptStr, Long.class);
        //设置lua脚本的返回值类型
//        longDefaultRedisScript.setResultType(Long.class);
        //设置lua脚本内容
//        longDefaultRedisScript.setScriptText("");
        //longDefaultRedisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource()));
        MemberResp memberResp = LoginUserUtils.get();
        //返回为1 令牌检验成功并删除， 返回0令牌校验失败
        Long executeResult = stringRedisTemplate.execute(longDefaultRedisScript, Collections.singletonList(OrderConstant.getOrderTokenKey(memberResp.getId().toString())), submitOrderVO.getOrderToken());
        if (executeResult != null && executeResult == 0) {
            submitOrderRespVO.setCode(2);
            return submitOrderRespVO;
        }
        //创建订单
        Long addrId = submitOrderVO.getAddrId();
        //获取收获地址
        R freightResponse = wareSkuFeignService.getFreight(addrId);
        if (freightResponse.getCode() != 0) {
            submitOrderRespVO.setCode(3);
            return submitOrderRespVO;
        }
        FreightResp freightResp = freightResponse.getData(new TypeReference<FreightResp>() {
        });
        if (freightResp == null) {
            submitOrderRespVO.setCode(4);
            return submitOrderRespVO;
        }
        //获取选择订单信息
        R checkCatItem = cartFeignService.getCheckCatItem();
        if (checkCatItem.getCode() != 0) {
            submitOrderRespVO.setCode(5);
            return submitOrderRespVO;
        }
        List<CartItemResp> cartItemResp = checkCatItem.getData(new TypeReference<List<CartItemResp>>() {
        });
        if (cartItemResp == null || cartItemResp.isEmpty()) {
            submitOrderRespVO.setCode(6);
            return submitOrderRespVO;
        }
        //构建订单和订单项
        CreateOrderTo createOrderTo = createOrder(cartItemResp, freightResp);
        //校验价格
        Boolean compareResult = computePrice(submitOrderVO, createOrderTo);
        if (!compareResult) {
            submitOrderRespVO.setCode(7);
            return submitOrderRespVO;
        }
        //保存订单和订单项
        saveOrder(createOrderTo);
        //锁定库存
        boolean wareStockLocked = wareStockLock(createOrderTo);
        if (!wareStockLocked) {
            throw new OrderItemLockException("库存锁定失败");
        }
        submitOrderRespVO.setCode(0);
        submitOrderRespVO.setOrder(createOrderTo.getOrder());
        //订单创建成功，发送消息过期自动关单
        rabbitTemplate.convertAndSend(OrderRabbitConstant.ORDER_EVENT_EXCHANGE, OrderRabbitConstant.CREATE_ORDER_ROUTING_KEY, createOrderTo.getOrder());
        return submitOrderRespVO;
    }

    @Override
    public OrderEntity getOrderByOrderSn(String orderSn) {
        return getOne(new LambdaQueryWrapper<OrderEntity>()
                .eq(OrderEntity::getOrderSn, orderSn));
    }

    @Override
    public void releaseOrder(OrderEntity order) {
        OrderEntity orderEntity = getById(order.getId());
        if (Objects.isNull(orderEntity)) {
            throw new RuntimeException("订单不存在！");
        }
        //查看订单状态
        Integer status = orderEntity.getStatus();
        if (OrderStatusEnum.CREATE_NEW.getCode().equals(status)) {
            //取消订单
            OrderEntity updateOrder = new OrderEntity();
            updateOrder.setStatus(OrderStatusEnum.CANCLED.getCode());
            updateOrder.setId(orderEntity.getId());
            updateById(updateOrder);
            OrderTo orderTo = new OrderTo();
            BeanUtils.copyProperties(order, orderTo);
//            rabbitTemplate.convertAndSend(OrderRabbitConstant.STOCK_EVENT_EXCHANGE, OrderRabbitConstant.STOCK_RELEASE_ROUTING_KEY, orderTo);
            Message message = createMessage(orderTo);
            try {
                /*MqMessageEntity messageEntity = buildMqMessage(message, JSONObject.toJSONString(orderTo), OrderTo.class.getName());
                messageService.save(messageEntity);*/
                rabbitTemplate.convertAndSend(OrderRabbitConstant.ORDER_EVENT_EXCHANGE, OrderRabbitConstant.ORDER_RELEASE_OTHER_ROUTING_KEY, message);
                alipayTradeClose.closeTrade(order.getOrderSn());
            } catch (AmqpException e) {
                log.error("releaseOrder send message exception", e);
            }
        }

    }

    @Override
    public PageUtils listWithItem(Map<String, Object> params) {
        MemberResp memberResp = LoginUserUtils.get();
        if (memberResp == null) {
            throw new UserNotLoggedInException("登陆用户失效");
        }
        IPage<OrderEntity> page = this.page(
                new Query<OrderEntity>().getPage(params),
                new QueryWrapper<OrderEntity>().lambda()
                        .eq(OrderEntity::getMemberId, memberResp.getId())
        );
        List<OrderEntity> records = page.getRecords();
        // 如果查询结果非空，进行订单项填充
        if (!CollectionUtils.isEmpty(records)) {
            List<OrderEntity> orderEntities = populateOrderItems(records);
            page.setRecords(orderEntities);
        }
        return new PageUtils(page);
    }

    // 将订单与订单项的关联填充逻辑抽离到一个单独的方法中
    private List<OrderEntity> populateOrderItems(List<OrderEntity> records) {
        return records.stream().peek(orderEntity -> {
            String orderSn = orderEntity.getOrderSn();
            List<OrderItemEntity> orderItems = orderItemService.getOrderItemsByOrderSn(orderSn);
            if (orderItems == null) {
                orderItems = new ArrayList<>();
            }
            orderEntity.setOrderItems(orderItems);
        }).collect(Collectors.toList());
    }

    private static MqMessageEntity buildMqMessage(Message message, String content, String classType) {
        return MqMessageEntity.builder()
                .messageId(IdWorker.getId())
                .content(content)
                .toExchange(OrderRabbitConstant.ORDER_EVENT_EXCHANGE)
                .routingKey(OrderRabbitConstant.ORDER_RELEASE_OTHER_ROUTING_KEY)
                .messageStatus(1)
                .classType(OrderTo.class.getName())
                .createTime(new Date())
                .updateTime(new Date())
                .build();
    }

    /**
     * 发送消息指定消息ID
     *
     * @param payload
     * @return
     */
    public Message createMessage(Object payload) {
        String messageId = UUID.randomUUID().toString();
        MessageProperties messageProperties = new MessageProperties();
        messageProperties.setMessageId(messageId);
        messageProperties.setContentType("application/json");
        return new Message(payload.toString().getBytes(StandardCharsets.UTF_8), messageProperties);
    }

    /**
     * 保存订单以及订单项
     *
     * @param createOrderTo
     */
    public void saveOrder(CreateOrderTo createOrderTo) {
        OrderEntity order = createOrderTo.getOrder();
        save(order);
        List<OrderItemEntity> orderItemEntities = createOrderTo.getOrderItems().stream().map(orderItem -> orderItem.setOrderId(order.getId())).collect(Collectors.toList());
        orderItemService.saveBatch(orderItemEntities);
    }


    private boolean wareStockLock(CreateOrderTo createOrderTo) {
        String orderSn = createOrderTo.getOrder().getOrderSn();
        List<CartItemResp> cartItems = createOrderTo.getOrderItems().stream().map(orderItem -> {
            Long skuId = orderItem.getSkuId();
            Integer skuQuantity = orderItem.getSkuQuantity();
            return CartItemResp.builder()
                    .skuId(skuId)
                    .count(skuQuantity)
                    .build();
        }).collect(Collectors.toList());
        WareSkuLockVO wareSkuLockVO = WareSkuLockVO.builder()
                .orderSn(orderSn)
                .orderItems(cartItems)
                .build();
        R lockResponse = wareSkuFeignService.orderItemLock(wareSkuLockVO);
        if (lockResponse.getCode() != 0) {
            return false;
        }
        return true;
    }

    private Boolean computePrice(SubmitOrderVO submitOrderVO, CreateOrderTo createOrderTo) {
        BigDecimal payAmount = createOrderTo.getOrder().getPayAmount();
        BigDecimal payPrice = submitOrderVO.getPayPrice();
        return Math.abs(payAmount.subtract(payPrice).intValue()) < 0.01;
    }

    private CreateOrderTo createOrder(List<CartItemResp> cartItemResp, FreightResp freightResp) {
        //构建订单
        OrderEntity orderEntity = buildOrderEntity(freightResp);
        //构建订单项
        List<OrderItemEntity> orderItems = buildOrderItem(cartItemResp, orderEntity.getOrderSn());
        //计算促销活动
        calculatePromotional(freightResp, orderItems, orderEntity);
        return buildOrderToResult(orderEntity, orderItems);
    }

    /**
     * 构建订单创建结果
     *
     * @param orderEntity 订单
     * @param orderItems  订单项
     * @return createResultTo
     */
    private static CreateOrderTo buildOrderToResult(OrderEntity orderEntity, List<OrderItemEntity> orderItems) {
        return CreateOrderTo.builder()
                .order(orderEntity)
                .orderItems(orderItems)
                .payPrice(orderEntity.getPayAmount())
                .freightPrice(orderEntity.getFreightAmount())
                .build();
    }

    /**
     * 计算订单优惠信息
     *
     * @param freightResp 收获地址信息
     * @param orderItems  订单信息
     * @param orderEntity 订单项信息
     */
    private static void calculatePromotional(FreightResp freightResp, List<OrderItemEntity> orderItems, OrderEntity orderEntity) {
        BigDecimal totalAmount = new BigDecimal("0.0");
        BigDecimal totalPromotionAmount = new BigDecimal("0.0");
        BigDecimal totalCouponAmount = new BigDecimal("0.0");
        BigDecimal totalIntegrationAmount = new BigDecimal("0.0");
        BigDecimal totalRealAmount = new BigDecimal("0.0");
        //积分和成长值
        Integer giftIntegration = 0;
        Integer giftGrowth = 0;
        for (OrderItemEntity orderItem : orderItems) {
            totalPromotionAmount = totalPromotionAmount.add(orderItem.getPromotionAmount());
            totalCouponAmount = totalCouponAmount.add(orderItem.getCouponAmount());
            totalIntegrationAmount = totalIntegrationAmount.add(orderItem.getIntegrationAmount());
            totalRealAmount = totalRealAmount.add(orderItem.getRealAmount());
            totalAmount = totalAmount.add(orderItem.getSkuPrice().multiply(new BigDecimal(orderItem.getSkuQuantity().toString())));
            giftGrowth += orderItem.getGiftIntegration();
            giftIntegration += orderItem.getGiftIntegration();
        }
        BigDecimal payAmount = totalAmount.add(freightResp.getFreightPrice());
        orderEntity.setPromotionAmount(totalPromotionAmount)
                .setIntegrationAmount(totalIntegrationAmount)
                .setCouponAmount(totalCouponAmount)
                .setDiscountAmount(totalRealAmount)
                .setTotalAmount(totalAmount)
                .setPayAmount(payAmount);
    }

    private List<OrderItemEntity> buildOrderItem(List<CartItemResp> cartItemResp, String orderSn) {
        return cartItemResp.stream().map(cartItem -> {
            OrderItemEntity orderItemEntity = new OrderItemEntity();
            //订单流水号
            orderItemEntity.setOrderSn(orderSn);
            //sku基本信息
            orderItemEntity.setSkuId(cartItem.getSkuId())
                    .setSkuName(cartItem.getTitle())
                    .setSkuPic(cartItem.getImage())
                    .setSkuPrice(cartItem.getPrice())
                    .setSkuQuantity(cartItem.getCount());
            if (cartItem.getSkuAttrs() != null && !cartItem.getSkuAttrs().isEmpty()) {
                String skuAttrVals = org.springframework.util.StringUtils.collectionToDelimitedString(cartItem.getSkuAttrs(), ";");
                orderItemEntity.setSkuAttrsVals(skuAttrVals);
            }
            //spu信息
            R skuInfoResponse = productFeignService.getSpuInfoBySkuId(cartItem.getSkuId());
            if (skuInfoResponse.getCode() == 0) {
                SpuInfoVO spuInfoVO = skuInfoResponse.getData(new TypeReference<SpuInfoVO>() {
                });
                if (spuInfoVO != null) {
                    orderItemEntity.setSpuId(spuInfoVO.getId())
                            .setSpuName(spuInfoVO.getSpuName())
                            .setSpuBrand(spuInfoVO.getBrandId().toString());
                }
            }
            //积分和成长值
            BigDecimal giftIntegration = cartItem.getTotalPrice();
            BigDecimal giftGrowth = cartItem.getTotalPrice();
            //促销信息
            BigDecimal promotionAmount = new BigDecimal("0.0");
            BigDecimal couponAmount = new BigDecimal("0.0");
            BigDecimal integrationAmount = new BigDecimal("0.0");
            BigDecimal realAmount = new BigDecimal("0.0")
                    .subtract(promotionAmount)
                    .subtract(couponAmount)
                    .subtract(integrationAmount);
            orderItemEntity.setPromotionAmount(promotionAmount)
                    .setCouponAmount(couponAmount)
                    .setIntegrationAmount(integrationAmount)
                    .setRealAmount(realAmount)
                    .setGiftIntegration(giftIntegration.intValue())
                    .setGiftGrowth(giftGrowth.intValue());
            return orderItemEntity;
        }).collect(Collectors.toList());
    }

    /**
     * 构建订单
     *
     * @param freightResp 收货人相关信息
     * @return orderEntity
     */
    private static OrderEntity buildOrderEntity(FreightResp freightResp) {
        MemberResp memberResp = LoginUserUtils.get();
        //生成订单流水号 时间 + Id
        String orderSn = IdWorker.getTimeId();
        OrderEntity orderEntity = new OrderEntity();
        //订单基本信息
        orderEntity.setOrderSn(orderSn)
                .setMemberId(memberResp.getId())
                .setMemberUsername(memberResp.getUsername())
                .setCreateTime(new Date())
                .setPayType(1)
                .setSourceType(1)
                .setStatus(0)
                .setAutoConfirmDay(7);
        //收货人相关信息
        ReceiveAddressResp address = freightResp.getAddress();
        orderEntity.setReceiverName(address.getName())
                .setFreightAmount(freightResp.getFreightPrice())
                .setReceiverPhone(address.getPhone())
                .setBillReceiverEmail(address.getPostCode())
                .setReceiverProvince(address.getProvince())
                .setReceiverCity(address.getCity())
                .setReceiverRegion(address.getRegion())
                .setReceiverDetailAddress(address.getDetailAddress())
                .setConfirmStatus(0)
                .setDeleteStatus(0);
        return orderEntity;
    }

    private String getUUIDToken() {
        return UUID.randomUUID().toString();
    }

    private static MemberResp getLoginUserInfo() {
        return LoginUserUtils.get();
    }

    //同一个对象内事务方法互调默认失效，原因 绕过了代理对象
    //事务使用代理对象来控制的
    //a事务的所有设置就传播到了和他公用一个事务的方法
    @Transactional(isolation = Isolation.REPEATABLE_READ, timeout = 50, propagation = Propagation.REQUIRED)
    public void a() {
        //此时a方法内抛出的异常b方法也会回滚，因为是在一个事物下面
        //但是c不会因为c开启了一个新的事务
        OrderServiceImpl orderService = (OrderServiceImpl) AopContext.currentProxy();
        orderService.a();
        orderService.b();
//        b();
//        c();
    }

    @Transactional(propagation = Propagation.REQUIRED)
    public void b() {

    }

    @Transactional(isolation = Isolation.DEFAULT, timeout = 20, propagation = Propagation.REQUIRES_NEW)
    public void c() {

    }

}